/**
 * CS543 HW4 Joseph Meyer
 */
package edu;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.commons.io.FileUtils;

import edu.src.address.Address;
import edu.src.address.AddressReaderUtils;
import edu.src.hardware.MemorySize;
import edu.src.hardware.PhysicalMemory;
import edu.src.hardware.TranslationLookasideBuffer;
import edu.src.logger.Logger;
import edu.src.paging.PageResult;
import edu.src.paging.PageTable;

/**
 * This project consists of writing a program that translates logical to physical
	addresses for a virtual address space of size 2 16 = 65,536 bytes. Your program
	will read from a file containing logical addresses and, using a TLB as well as
	a page table, will translate each logical address to its corresponding physical
	address and output the value of the byte stored at the translated physical
	address. The goal behind this project is to simulate the steps involved in
	translating logical to physical addresses.
		-pg458
 * @author meyer
 *
 */
public class MemoryManager {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		Logger logger = new Logger(MemoryManager.class);

		runAddresses1_64K(logger);
		clear();
		runAddresses1_32K(logger);
		clear();
		runAddresses2(logger);
		
	}
	
	/**
	 * Reset TranslationLookasideBuffer for next run 
	 */
	private static void clear() {
		TranslationLookasideBuffer.getInstance().emptyTLB();
	}
	
	/**
	 * Run all items from the standard input. 
	 * This uses "addresses.txt"
	 * @param logger - Logger for stack traces
	 */
	private static void runAddresses1_32K(Logger logger) {
		//The list of addresses / pages / offsets
		File f =new File("resources/addresses.txt");
		List<Address> addList = AddressReaderUtils.readAddressesFromFile(f);
		System.out.println("\n==========================================\n");
		System.out.println("Beginning Run of : " + f.getName() + " using 32K physical memory");
		
		//Set up the Physical Memory space
		PhysicalMemory pMemory = PhysicalMemory.getInstance();
		pMemory.setMemSize(MemorySize.K32);
		
		//Get the TLB and Page Table reference to use to get values from memory
		PageTable pt = PageTable.getInstance();
		TranslationLookasideBuffer TLB = TranslationLookasideBuffer.getInstance();
		File output = new File("resources/generated_addresses_32k_output.txt");
		StringBuilder sb = new StringBuilder();
		int counter = 0;
		for (Address a : addList) {
			//first check the TLB
			PageResult tlbPr = TLB.checkTLB(a);
			if (tlbPr == null) {
				//with a miss, go to the PageTable
				PageResult pr = pt.findEntry(a);
				sb.append(pr.formattedOutput());
				
				//add this entry to the TLB for future use
				TLB.addOrReplaceTLB(a); 
			} else {
				//page was loaded from the TLB
				sb.append(tlbPr.formattedOutput());
			}
			counter++;
		}
		sb.append("Number of Translated Addresses = ");
		sb.append(counter);
		
		String actual = sb.toString();
		try {
			FileUtils.write(output, actual);
		} catch (IOException e) {
			logger.error("Could not generate final output", e);
		}
		
		System.out.println("Generated output information at: " + output.getName());
		System.out.println("Page-fault rate: " + pt.getFaultRate() +"%");
		System.out.println("Page faults: " + pt.getFaultCount() +", and Page Accesses: " + pt.getAccessCount());
		System.out.println("TLB hits: "+ TLB.getHit() +",and hit rate: " + TLB.getHitRate() + "%"); 
	}
	
	/**
	 * Run all items from the standard input. 
	 * This uses "addresses.txt"
	 * @param logger - Logger for stack traces
	 */
	private static void runAddresses1_64K(Logger logger) {
		//The list of addresses / pages / offsets
		File f =new File("resources/addresses.txt");
		List<Address> addList = AddressReaderUtils.readAddressesFromFile(f);
		System.out.println("\n==========================================\n");
		System.out.println("Beginning Run of : " + f.getName() + " using 64K physical memory");
		//Set up the Physical Memory space
		PhysicalMemory pMemory = PhysicalMemory.getInstance();
		pMemory.setMemSize(MemorySize.K64);
				
		//Get the TLB and Page Table reference to use to get values from memory
		PageTable pt = PageTable.getInstance();
		TranslationLookasideBuffer TLB = TranslationLookasideBuffer.getInstance();
		File output = new File("resources/generated_addresses_64k_output.txt");
		StringBuilder sb = new StringBuilder();
		int counter = 0;
		for (Address a : addList) {
			//first check the TLB
			PageResult tlbPr = TLB.checkTLB(a);
			if (tlbPr == null) {
				//with a miss, go to the PageTable
				PageResult pr = pt.findEntry(a);
				sb.append(pr.formattedOutput());
				//add this entry to the TLB for future use
				TLB.addOrReplaceTLB(a); 
			} else {
				//page was loaded from the TLB
				sb.append(tlbPr.formattedOutput());
				
			}
			counter++;
		}
		sb.append("Number of Translated Addresses = ");
		sb.append(counter);
		
		String actual = sb.toString();
		try {
			FileUtils.write(output, actual);
		} catch (IOException e) {
			logger.error("Could not generate final output", e);
		}
		
		System.out.println("Generated output information at: " + output.getName());
		System.out.println("Page-fault rate: " + pt.getFaultRate() +"%");
		System.out.println("Page faults: " + pt.getFaultCount() +", and Page Accesses: " + pt.getAccessCount());
		System.out.println("TLB hit rate: " + TLB.getHitRate() + "%");
		System.out.println("TLB hits: "+ TLB.getHit() +",and hit rate: " + TLB.getHitRate() + "%"); 
	}

	/**
	 * Run all items from the expanded input 
	 * This uses "addresses2.txt"
	 * @param logger - Logger for stack traces
	 */
	private static void runAddresses2(Logger logger) {
		//The list of addresses / pages / offsets
		File f =new File("resources/addresses2.txt");
		List<Address> addList = AddressReaderUtils.readAddressesFromFile(f);
		System.out.println("\n==========================================\n");
		System.out.println("Beginning Run of : " + f.getName() + " using 32K physical memory");
		//Set up the Physical Memory space
		PhysicalMemory pMemory = PhysicalMemory.getInstance();
		pMemory.setMemSize(MemorySize.K32);
		
		//Get the TLB and Page Table reference to use to get values from memory
		PageTable pt = PageTable.getInstance();
		TranslationLookasideBuffer TLB = TranslationLookasideBuffer.getInstance();
		File output = new File("resources/generated_addresses2_output.txt");
		StringBuilder sb = new StringBuilder();
		int counter = 0;
		for (Address a : addList) {
			//first check the TLB
			PageResult tlbPr = TLB.checkTLB(a);
			if (tlbPr == null) {
				//with a miss, go to the PageTable
				PageResult pr = pt.findEntry(a);
				sb.append(pr.formattedOutput());
				
				//add this entry to the TLB for future use
				TLB.addOrReplaceTLB(a);
			} else {
				//page was loaded from the TLB
				sb.append(tlbPr.formattedOutput());
			}
			counter++;
		}
		sb.append("Number of Translated Addresses = ");
		sb.append(counter);
		
		String actual = sb.toString();
		try {
			FileUtils.write(output, actual);
		} catch (IOException e) {
			logger.error("Could not generate final output", e);
		}
		
		System.out.println("Generated output information at: " + output.getName());
		System.out.println("Page-fault rate: " + pt.getFaultRate() +"%");
		System.out.println("Page faults: " + pt.getFaultCount() +", and Page Accesses: " + pt.getAccessCount());
		System.out.println("TLB hits: "+ TLB.getHit() +",and hit rate: " + TLB.getHitRate() + "%"); 
		System.out.println("Number of items written to the Backing Store: " + pt.getWrittenToStore());
	}

}
